package com.xujiang.common_adapter.internal;

import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;

import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * @author https://gist.github.com/darnmason/7bbf8beae24fe7296c8a
 * @author xujiang
 * @version 2017-07-04 15:26
 */
public class HeaderViewRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private static final int HEADERS_START = Integer.MIN_VALUE;
    private static final int FOOTERS_START = Integer.MIN_VALUE + 10;
    private static final int ITEMS_START = Integer.MIN_VALUE + 20;
    private static final int ADAPTER_MAX_TYPES = 100;

    private RecyclerView.Adapter<RecyclerView.ViewHolder> mWrappedAdapter;
    private List<View> mHeaderViews, mFooterViews;
    private Map<Class, Integer> mItemTypesOffset;

    /**
     * 判断头布局是否存在
     */
    public boolean hasHeader() {
        return mHeaderViews != null && !mHeaderViews.isEmpty();
    }

    /**
     * 判断尾布局是否存在
     */
    public boolean hasFooter() {
        return mFooterViews != null && !mFooterViews.isEmpty();
    }

    /**
     * Construct a new header view recycler adapter
     * 构造函数
     */
    public HeaderViewRecyclerAdapter() {
        mHeaderViews = new LinkedList<>();  //LinkedList类是双向列表,列表中的每个节点都包含了对前一个和后一个元素的引用.
        mFooterViews = new LinkedList<>();
        mItemTypesOffset = new LinkedHashMap<>();  //LinkedHashMap 是HashMap的一个子类，保存了记录的插入顺序，在用Iterator遍历LinkedHashMap时，
        // 先得到的记录肯定是先插入的.也可以在构造时用带参数，按照应用次数排序。在遍历的时候会比HashMap慢，不过有种情况例外，当HashMap容量很大，
        // 实际数据较少时，遍历起来可能会比 LinkedHashMap慢，因为LinkedHashMap的遍历速度只和实际数据有关，
        // 和容量无关，而HashMap的遍历速度和他的容量有关。
    }

    /**
     * Construct a new header view recycler adapter
     *
     * @param adapter The underlying adapter to wrap
     */
    @SuppressWarnings("unchecked")
    public HeaderViewRecyclerAdapter(RecyclerView.Adapter<? extends RecyclerView.ViewHolder> adapter) {
        this();
        setWrappedAdapter((RecyclerView.Adapter<RecyclerView.ViewHolder>) adapter);
    }

    /**
     * Add a static view to appear at the start of the RecyclerView. Headers are displayed in the
     * order they were added.
     *
     * @param views The header view to add all
     */
    public void addHeaderView(List<View> views) {
        if (views == null) {
            return;
        }
        mHeaderViews.addAll(views);
    }

    /**
     * Add a static view to appear at the end of the RecyclerView. Footers are displayed in the
     * order they were added.
     *
     * @param views The footer view to add all
     */
    public void addFooterView(List<View> views) {
        if (views == null) {
            return;
        }
        mFooterViews.addAll(views);
    }

    /**
     * Add a static view to appear at the start of the RecyclerView. Headers are displayed in the
     * order they were added.
     *
     * @param view The header view to add
     */
    public void addHeaderView(View view) {
        if (view == null) {
            return;
        }
        mHeaderViews.add(view);
    }

    /**
     * Add a static view to appear at the end of the RecyclerView. Footers are displayed in the
     * order they were added.
     *
     * @param view The footer view to add
     */
    public void addFooterView(View view) {
        if (view == null) {
            return;
        }
        mFooterViews.add(view);
    }

    /**
     * Replaces the underlying adapter, notifying RecyclerView of changes
     *
     * @param adapter The new adapter to wrap
     */
    @SuppressWarnings("unchecked")
    public void setAdapter(
            RecyclerView.Adapter<? extends RecyclerView.ViewHolder> adapter) {
        if (mWrappedAdapter != null && mWrappedAdapter.getItemCount() > 0) {
            notifyItemRangeRemoved(getHeaderCount(), mWrappedAdapter.getItemCount());
        }
        setWrappedAdapter((RecyclerView.Adapter<RecyclerView.ViewHolder>) adapter);
        notifyItemRangeInserted(getHeaderCount(), mWrappedAdapter.getItemCount());
    }

    public boolean isHeader(int position) {
        return hasHeader() && position < getHeaderCount();
    }

    public boolean isFooter(int position) {
        return hasFooter() && position >= (mWrappedAdapter.getItemCount() + getHeaderCount());
    }

    private void setWrappedAdapter(RecyclerView.Adapter<RecyclerView.ViewHolder> adapter) {
        if (mWrappedAdapter != null) {
            mWrappedAdapter.unregisterAdapterDataObserver(mDataObserver);  //解注册
        }
        mWrappedAdapter = adapter;
        Class adapterClass = mWrappedAdapter.getClass();
        if (!mItemTypesOffset.containsKey(adapterClass)) {
            putAdapterTypeOffset(adapterClass);
        }
        mWrappedAdapter.registerAdapterDataObserver(mDataObserver);  //重注册
    }

    private void putAdapterTypeOffset(Class adapterClass) {
        mItemTypesOffset.put(adapterClass,
                ITEMS_START + mItemTypesOffset.size() * ADAPTER_MAX_TYPES);
    }

    private int getAdapterTypeOffset() {
        return mItemTypesOffset.get(mWrappedAdapter.getClass());
    }

    @Override
    public int getItemViewType(int position) {
        int itemCount = mWrappedAdapter.getItemCount();
        if (isHeader(position)) {
            return HEADERS_START + position;
        } else if (isFooter(position)) {
            return FOOTERS_START + (position - getHeaderCount() - itemCount);
        } else {
            return mWrappedAdapter.getItemViewType(position - getHeaderCount());
        }
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (viewType <= HEADERS_START + getHeaderCount()) {
            return new StaticViewHolder(mHeaderViews.get(viewType - HEADERS_START));
        } else if (viewType <= FOOTERS_START + getFooterCount()) {
            return new StaticViewHolder(mFooterViews.get(viewType - FOOTERS_START));
        } else {
            return mWrappedAdapter.onCreateViewHolder(parent, viewType);
        }
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        if (isFooter(position) || isHeader(position)) {
            return;
        }
        mWrappedAdapter.onBindViewHolder(holder, position - getHeaderCount());
    }

    @Override
    public int getItemCount() {
        return getHeaderCount() + getFooterCount() + getWrappedItemCount();
    }

    /**
     * @return The item count in the underlying adapter
     */
    public int getWrappedItemCount() {
        return mWrappedAdapter.getItemCount();
    }

    //http://blog.csdn.net/gdutxiaoxu/article/details/51698261
    //http://blog.csdn.net/gdutxiaoxu/article/details/51824769
    private RecyclerView.AdapterDataObserver mDataObserver = new RecyclerView.AdapterDataObserver() {
        @Override
        public void onChanged() {
            super.onChanged();
            notifyDataSetChanged();
        }

        @Override
        public void onItemRangeChanged(int positionStart, int itemCount) {
            super.onItemRangeChanged(positionStart, itemCount);
            notifyItemRangeChanged(positionStart + getHeaderCount(), itemCount);
        }

        @Override
        public void onItemRangeInserted(int positionStart, int itemCount) {
            super.onItemRangeInserted(positionStart, itemCount);
            notifyItemRangeInserted(positionStart + getHeaderCount(), itemCount);
        }

        @Override
        public void onItemRangeRemoved(int positionStart, int itemCount) {
            super.onItemRangeRemoved(positionStart, itemCount);
            notifyItemRangeRemoved(positionStart + getHeaderCount(), itemCount);
        }

        @Override
        public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
            super.onItemRangeMoved(fromPosition, toPosition, itemCount);
            int hCount = getHeaderCount();
            notifyItemRangeChanged(fromPosition + hCount, toPosition + hCount + itemCount);
        }
    };

    private static class StaticViewHolder extends RecyclerView.ViewHolder {

        public StaticViewHolder(View itemView) {
            super(itemView);
        }
    }

    /**
     * @return The number of header views added
     */
    public int getHeaderCount() {
        return mHeaderViews.size();
    }

    /**
     * @return The number of footer views added
     */
    public int getFooterCount() {
        return mFooterViews.size();
    }

    /**
     * @return header views
     */
    public List<View> getHeaderViews() {
        return mHeaderViews;
    }

    /**
     * @return footer views
     */
    public List<View> getFooterViews() {
        return mFooterViews;
    }

    public RecyclerView.Adapter<RecyclerView.ViewHolder> getWrappedAdapter() {
        return mWrappedAdapter;
    }

}
